﻿// <copyright file="NeptuneDBVirtualRainSource.cs" company="City of Portland, BES-ASM">
// </copyright>
// <summary>NeptuneDBVirtualRainSource class</summary>

namespace BESASM.EMGAATS.DataAccess
{
  using System;
  using System.Collections.Generic;
  using System.Data.SqlClient;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;
  using BusinessClasses;

  /// <summary>
  /// Manages a Neptune SQL Server database virtual rain source
  /// </summary>
  public class NeptuneDBVirtualRainSource : IRainSource, IDisposable
  {
    private const string SQLDateFormat = "yyyy-MM-ddTHH:mm:ss";
    private DateTime startDateTime;
    private DateTime endDateTime;
    private int timeStepSec;

    private Dictionary<int, IPoint> raingages;

    private string connectionString = "SERVER=BESDBPROD2;DATABASE=NEPTUNE;Trusted_Connection=Yes";
    private SqlConnection conn;

    /// <summary>
    /// Initializes a new instance of the <see cref="NeptuneDBVirtualRainSource"/> class
    /// </summary>
    /// <param name="startDateTime">The starting date/time of the storm</param>
    /// <param name="endDateTime">The ending date/time of the storm</param>
    /// <param name="timeStepSec">The desired time step in seconds</param>
    /// <param name="locations">A mapping of raingage IDs and their corresponding locations</param>
    public NeptuneDBVirtualRainSource(
      DateTime startDateTime, 
      DateTime endDateTime,
      int timeStepSec, 
      Dictionary<int, IPoint> locations)
    {
      int[] validTimeSteps = new int[] { 60, 300, 900, 3600 };

      if (!validTimeSteps.Contains(timeStepSec))
      {
        throw new ArgumentException(
          string.Format(
            "{0} is an invalid time step; 60, 300, 900, and 3600 seconds are allowed.",
            timeStepSec));
      }

      this.startDateTime = startDateTime;
      this.endDateTime = endDateTime;
      this.timeStepSec = timeStepSec;
      this.raingages = locations;

      this.conn = new SqlConnection(this.connectionString);
      this.conn.Open();
    }

    /// <summary>
    /// Gets a list of the raingauges and their coordinates
    /// </summary>
    public Dictionary<int, IPoint> Locations
    {
      get
      {
        return this.raingages;
      }
    }

    /// <summary>
    /// Gets a list of the raingages
    /// </summary>
    public List<int> LocationIDs
    {
      get
      {
        var l = from r in this.raingages
                orderby r.Key
                select r.Key;

        return l.ToList();
      }
    }

    /// <summary>
    /// Gets the enumeration of hyetograph points for the raingage
    /// </summary>
    /// <param name="raingage">The raingage ID of interest</param>
    /// <returns>The enumeration of hyetograph points</returns>
    public IEnumerable<HyetographPoint> Rainfall(int raingage)
    {
      string timestepString;
      switch (this.timeStepSec)
      {
        case 60:
          timestepString = "@interval=1";
          break;
        case 300:
          timestepString = "@interval=5";
          break;
        case 900:
          timestepString = "@interval=15";
          break;
        case 3600:
          timestepString = "@interval=60";
          break;
        default:
          timestepString = "@interval=5";
          break;
      }

      string commandString;
      try
      {
        commandString = string.Format(
          "EXEC USP_MODEL_VIRTUAL_RAIN @start_time='{0}', @end_time='{1}', @x_coord={2}, @y_coord={3}, {4}, @include_zeros=1",
          this.startDateTime.ToString(SQLDateFormat),
          this.endDateTime.ToString(SQLDateFormat),
          this.raingages[raingage].X,
          this.raingages[raingage].Y,
          timestepString);
      }
      catch (KeyNotFoundException)
      {
        throw new ArgumentException(string.Format("Raingauge {0} does not exist for the time period", raingage));
      }

      SqlCommand getRainfall = new SqlCommand(commandString, this.conn);

      using (SqlDataReader reader = getRainfall.ExecuteReader())
      {
        while (reader.Read())
        {
          double rainfallDepthIn;
          double rainfallIntensityInPerHr;
          try
          {
            rainfallDepthIn = reader.GetDouble(2);
            rainfallIntensityInPerHr = rainfallDepthIn / ((this.timeStepSec / 60.0) / 60.0);
          }
          catch (System.Data.SqlTypes.SqlNullValueException)
          {
            throw new ArgumentException(string.Format("Raingauge {0} does not exist for the time period", raingage));
          }

          yield return new HyetographPoint(reader.GetDateTime(0), rainfallIntensityInPerHr);
        }
      }
    }

    /// <summary>
    /// Disposes the object
    /// </summary>
    public void Dispose()
    {
      this.conn.Close();
    }
  }
}
